/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif

#include "test_driver.h"

#define private public
#include "device/driver.h"
#undef private
#include <gtest/gtest.h>
#include <mc/exception.h>

#include <cstring>
#include <dlfcn.h>
#include <memory>

using namespace devmon::test;

namespace {
driver_handle_t stub_ctor(void*, const char*) {
    return reinterpret_cast<driver_handle_t>(0x1);
}

status_t stub_init(driver_handle_t, void*, void*) {
    return STATUS_OK;
}

status_t stub_start(driver_handle_t) {
    return STATUS_OK;
}

status_t stub_stop(driver_handle_t) {
    return STATUS_OK;
}

std::shared_ptr<device_driver_t> make_stub_device(const char* name) {
    return std::make_shared<device_driver_t>(
        device_driver_t{name, stub_ctor, stub_init, stub_start, stub_stop});
}

devmon::device_drivers make_stub_driver_list(const char* name) {
    devmon::device_drivers drivers;
    drivers.emplace_back(make_stub_device(name));
    return drivers;
}

void* open_libdl_handle() {
    void* handle = dlopen("libdl.so.2", RTLD_LAZY);
    if (handle == nullptr) {
        handle = dlopen("libdl.so", RTLD_LAZY);
    }
    return handle;
}

bool            g_register_driver_force_error  = true;
bool            g_register_driver_return_empty = false;
device_driver_t g_stub_device_entry  = {"StubDevice", stub_ctor, stub_init, stub_start, stub_stop};
void*           g_fake_driver_handle = reinterpret_cast<void*>(0xDEADBEEF);

class tracking_device_driver : public devmon::device_driver {
public:
    explicit tracking_device_driver(bool* destroyed_flag)
        : devmon::device_driver("tracking_driver"), m_destroyed_flag(destroyed_flag) {
    }

    ~tracking_device_driver() {
        if (m_destroyed_flag != nullptr) {
            *m_destroyed_flag = true;
        }
    }

private:
    bool* m_destroyed_flag;
};
} // namespace

extern "C" __attribute__((visibility("default"))) __attribute__((used)) status_t
register_device_driver(device_driver_t** devices, uint8_t* count) {
    if (g_register_driver_force_error) {
        if (devices != nullptr) {
            *devices = nullptr;
        }
        if (count != nullptr) {
            *count = 0;
        }
        return STATUS_ERROR;
    }
    if (g_register_driver_return_empty) {
        if (devices != nullptr) {
            *devices = nullptr;
        }
        if (count != nullptr) {
            *count = 0;
        }
        return STATUS_OK;
    }
    if (devices != nullptr) {
        *devices = &g_stub_device_entry;
    }
    if (count != nullptr) {
        *count = 1;
    }
    return STATUS_OK;
}
extern "C" {
driver_handle_t create_device(void* service, const char* name) {
    try {
        if (service == nullptr) {
            return nullptr;
        }

        test_device* device = new test_device();
        device->set_object_name(name);
        device->set_service(static_cast<mc::engine::service*>(service));
        return (void*)device;
    } catch (const std::exception& e) {
        elog("Error creating device: ${error}", ("error", e.what()));
        return nullptr;
    }
}

status_t init_device(driver_handle_t device, void* csr_object, void* connector) {
    test_device*      device_object  = static_cast<test_device*>(device);
    mc::mutable_dict* csr_object_ptr = static_cast<mc::mutable_dict*>(csr_object);
    mc::dict*         connector_ptr  = static_cast<mc::dict*>(connector);
    bool              ret            = device_object->init(*csr_object_ptr, *connector_ptr);
    if (!ret) {
        return STATUS_ERROR;
    }
    return STATUS_OK;
}
status_t start_device(driver_handle_t device) {
    test_device* device_object = static_cast<test_device*>(device);
    device_object->start();
    return STATUS_OK;
}

status_t stop_device(driver_handle_t device) {
    test_device* device_object = static_cast<test_device*>(device);
    device_object->stop();
    return STATUS_OK;
}
}

namespace {
using dlsym_func = void* (*)(void*, const char*);

dlsym_func resolve_real_dlsym() {
    static dlsym_func real = nullptr;
    if (real != nullptr) {
        return real;
    }
#ifdef RTLD_NEXT
    real = reinterpret_cast<dlsym_func>(::dlvsym(RTLD_NEXT, "dlsym", "GLIBC_2.34"));
    if (real == nullptr) {
        real = reinterpret_cast<dlsym_func>(::dlvsym(RTLD_NEXT, "dlsym", "GLIBC_2.2.5"));
    }
#endif
    if (real == nullptr) {
        void* libdl_handle = dlopen("libdl.so.2", RTLD_LAZY | RTLD_LOCAL);
        if (libdl_handle == nullptr) {
            libdl_handle = dlopen("libdl.so", RTLD_LAZY | RTLD_LOCAL);
        }
        if (libdl_handle != nullptr) {
            real = reinterpret_cast<dlsym_func>(::dlvsym(libdl_handle, "dlsym", "GLIBC_2.34"));
            if (real == nullptr) {
                real = reinterpret_cast<dlsym_func>(::dlvsym(libdl_handle, "dlsym", "GLIBC_2.2.5"));
            }
            if (real == nullptr) {
                real = reinterpret_cast<dlsym_func>(::dlsym(libdl_handle, "dlsym"));
            }
        }
    }
    return real;
}
} // namespace

extern "C" void* dlsym(void* handle, const char* symbol) {
    if (handle == g_fake_driver_handle && std::strcmp(symbol, "register_device_driver") == 0) {
        return reinterpret_cast<void*>(&register_device_driver);
    }
    auto* real = resolve_real_dlsym();
    if (real == nullptr) {
        return nullptr;
    }
    return real(handle, symbol);
}

namespace devmon::test {

bool test_device::init(mc::mutable_dict& csr_object, const mc::dict& connector) {
    try {
        from_variant(csr_object, *this);
        // 统一基于CSR字典判断并补齐 DeviceName，避免访问property读取接口
        bool has_test_iface_name = false;
        if (csr_object.contains("bmc.dev.TestInterface") &&
            csr_object["bmc.dev.TestInterface"].is_dict()) {
            auto t = csr_object["bmc.dev.TestInterface"].as_dict();
            if (t.contains("DeviceName")) {
                auto v = t["DeviceName"].as<std::string>();
                if (!v.empty()) {
                    has_test_iface_name = true;
                }
            }
        }

        if (!has_test_iface_name) {
            // 尝试从 PCIeDevice 回填
            if (csr_object.contains("bmc.dev.PCIeDevice") &&
                csr_object["bmc.dev.PCIeDevice"].is_dict()) {
                auto pcie = csr_object["bmc.dev.PCIeDevice"].as_dict();
                if (pcie.contains("DeviceName")) {
                    auto v = pcie["DeviceName"].as<std::string>();
                    if (!v.empty()) {
                        m_test_device_interface.DeviceName = v;
                        has_test_iface_name                = true;
                    }
                }
            }
        }

        // 仍为空则使用对象名兜底，避免路径缺段
        if (!has_test_iface_name) {
            m_test_device_interface.DeviceName = std::string(get_object_name());
        }

        // 当 DeviceName 为引用扩展时，如果计算得到的对象路径已存在，则为当前对象设置唯一后缀路径
        // 这样可避免多个设备引用同一源对象名导致的路径冲突，同时不改变属性的引用语义
        auto name_value = m_test_device_interface.DeviceName.get_value();
        if (name_value.is_extension() && get_service() != nullptr) {
            // 计算期望路径：/bmc/dev/Systems/${SystemId}/test_device/${DeviceNameRefName}
            std::string ref_name;
            try {
                auto* ref_obj = name_value.as<mc::engine::ref_object*>();
                if (ref_obj != nullptr) {
                    ref_name = std::string(ref_obj->get_object_name());
                }
            } catch (...) {
                // 忽略异常，无法获取引用名时跳过去重逻辑
            }

            if (!ref_name.empty()) {
                uint8_t sys_id      = 0;
                auto    sysid_value = m_system_id.get_value();
                if (sysid_value.is_uint8()) {
                    sys_id = sysid_value.as<uint8_t>();
                }
                std::string desired_path = std::string("/bmc/dev/Systems/") +
                                           std::to_string(sys_id) + "/test_device/" + ref_name;
                auto& table  = get_service()->get_object_table();
                bool  exists = false;
                table.query_object(mc::db::query_builder(), [&](mc::db::object_base& obj) {
                    auto& abstract_obj = static_cast<mc::engine::abstract_object&>(obj);
                    if (std::string(abstract_obj.get_object_path()) == desired_path) {
                        exists = true;
                        return false; // 终止遍历
                    }
                    return true;
                });
                if (exists) {
                    // 已存在则追加对象名形成唯一路径
                    set_object_path(desired_path + std::string("_") +
                                    std::string(get_object_name()));
                }
            }
        }
    } catch (const std::exception& e) {
        ilog("test_device init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }
    return true;
}

bool test_device::start() {
    ilog("test_device start");
    return true;
}

bool test_device::stop() {
    ilog("test_device stop");
    return true;
}

device_driver_t* test_driver::create(std::string& device_name) {
    device_driver_t* device_driver = (device_driver_t*)malloc(sizeof(device_driver_t));

    // 为 device_name 分配稳定存储，避免悬挂指针
    char* stable_name = (char*)malloc(device_name.size() + 1);
    std::memcpy(stable_name, device_name.c_str(), device_name.size() + 1);

    // 使用 placement new 在已分配的内存上构造对象
    new (device_driver) device_driver_t{.device_name = stable_name,
                                        .ctor        = create_device,
                                        .init        = init_device,
                                        .start       = start_device,
                                        .stop        = stop_device};

    return device_driver;
}

} // namespace devmon::test

MC_REFLECT(devmon::test::test_device_interface,
           ((DeviceName, "DeviceName"))((PortCount, "PortCount"))((NetDevFuncType,
                                                                   "NetDevFuncType")))
MC_REFLECT(devmon::test::test_device,
           ((m_system_id, "SystemId"))((m_test_device_interface, "bmc.dev.TestInterface")))

TEST(DeviceDriverTest, SetDeviceDriverAndGetDeviceSuccess) {
    devmon::device_driver driver("test_driver_case");
    auto                  drivers = make_stub_driver_list("TestDevice");
    driver.set_device_driver(&drivers);

    EXPECT_EQ(driver.get_devices().size(), 1);

    std::string query = "TestDevice";
    auto&       entry = driver.get_device(query);
    EXPECT_STREQ(entry.device_name, "TestDevice");
    EXPECT_NE(entry.ctor, nullptr);
}

TEST(DeviceDriverTest, GetDeviceThrowsWhenNotLoaded) {
    devmon::device_driver driver("empty_driver");
    std::string           query = "Any";
    EXPECT_THROW(driver.get_device(query), mc::invalid_arg_exception);
}

TEST(DeviceDriverTest, GetDeviceThrowsWhenDeviceMissing) {
    devmon::device_driver driver("test_driver_case");
    auto                  drivers = make_stub_driver_list("ExistingDevice");
    driver.set_device_driver(&drivers);

    std::string missing = "MissingDevice";
    EXPECT_THROW(driver.get_device(missing), mc::invalid_arg_exception);
}

TEST(DeviceDriverTest, GetNameReturnsConfiguredValue) {
    devmon::device_driver driver("driver_name");
    EXPECT_EQ(driver.get_name(), std::string_view("driver_name"));
}

TEST(DeviceDriverTest, LoadMissingLibraryThrowsSystemException) {
    devmon::device_driver driver("nonexistent_driver");
    EXPECT_THROW(driver.load("/tmp/devmon_missing_path"), mc::system_exception);
}

TEST(DeviceDriverTest, RegisterDriverThrowsWhenSymbolMissing) {
    devmon::device_driver driver("missing_symbol_driver");
    void*                 handle = open_libdl_handle();
    ASSERT_NE(handle, nullptr);
    driver.m_driver_handle = handle;

    EXPECT_THROW(driver.register_driver(), mc::bad_function_call_exception);

    dlclose(handle);
    driver.m_driver_handle = nullptr;
}

TEST(DeviceDriverTest, RegisterDriverThrowsWhenFunctionReturnsError) {
    devmon::device_driver driver("invalid_status_driver");
    void*                 handle = dlopen(nullptr, RTLD_LAZY);
    ASSERT_NE(handle, nullptr);
    driver.m_driver_handle = handle;

    g_register_driver_force_error = true;
    EXPECT_THROW(driver.register_driver(), mc::bad_function_call_exception);

    dlclose(handle);
    driver.m_driver_handle = nullptr;
}

TEST(DeviceDriverTest, RegisterDriverPopulatesDevicesOnSuccess) {
    devmon::device_driver driver("success_driver");
    driver.m_driver_handle = g_fake_driver_handle;

    g_register_driver_force_error = false;
    EXPECT_NO_THROW(driver.register_driver());
    g_register_driver_force_error = true;

    ASSERT_EQ(driver.get_devices().size(), 1);
    std::string query = "StubDevice";
    auto&       entry = driver.get_device(query);
    EXPECT_STREQ(entry.device_name, "StubDevice");

    driver.m_driver_handle = nullptr;
}

TEST(DeviceDriverTest, RegisterDriverHandlesZeroDevicesGracefully) {
    devmon::device_driver driver("zero_devices_driver");
    driver.m_driver_handle = g_fake_driver_handle;

    g_register_driver_force_error  = false;
    g_register_driver_return_empty = true;
    EXPECT_NO_THROW(driver.register_driver());
    EXPECT_TRUE(driver.get_devices().empty());

    std::string query = "AnyDevice";
    EXPECT_THROW(driver.get_device(query), mc::invalid_arg_exception);

    g_register_driver_return_empty = false;
    g_register_driver_force_error  = true;
    driver.m_driver_handle         = nullptr;
}

TEST(DeviceDriverTest, UnloadWithoutPriorLoadIsNoop) {
    devmon::device_driver driver("noop_driver");
    EXPECT_NO_THROW(driver.unload());

    auto drivers = make_stub_driver_list("AnyDevice");
    driver.set_device_driver(&drivers);
    EXPECT_NO_THROW(driver.unload());
}

TEST(DeviceDriverTest, UnloadAfterSuccessfulLoadClosesHandle) {
    devmon::device_driver driver("loaded_driver");
    void*                 handle = dlopen(nullptr, RTLD_LAZY);
    ASSERT_NE(handle, nullptr);
    driver.m_driver_handle = handle;

    EXPECT_NO_THROW(driver.unload());
    EXPECT_EQ(driver.m_driver_handle, nullptr);
}

TEST(DeviceDriverFactoryTest, LocalFactoryDestructorReleasesDrivers) {
    bool destroyed = false;
    {
        devmon::device_driver_factory local_factory;
        auto                          tracker =
            std::shared_ptr<devmon::device_driver>(new tracking_device_driver(&destroyed));
        local_factory.m_drivers.emplace("tracking_driver", tracker);
    }
    EXPECT_TRUE(destroyed);
}

TEST(DeviceDriverFactoryTest, SetDeviceDriverProvidesStubInstance) {
    auto& factory = devmon::device_driver_factory::get_instance();
    auto  drivers = make_stub_driver_list("FactoryDevice");

    factory.set_device_driver("factory_driver", &drivers);

    auto& driver = factory.get_device_driver("factory_driver");
    EXPECT_EQ(driver.get_devices().size(), 1);

    std::string query = "FactoryDevice";
    auto&       entry = driver.get_device(query);
    EXPECT_STREQ(entry.device_name, "FactoryDevice");

    factory.remove_device_driver("factory_driver");
}

TEST(DeviceDriverFactoryTest, RemoveDeviceDriverClearsEntry) {
    auto& factory = devmon::device_driver_factory::get_instance();
    auto  drivers = make_stub_driver_list("RemovableDevice");

    factory.set_device_driver("removable_driver", &drivers);
    factory.remove_device_driver("removable_driver");

    factory.set_driver_path("/tmp/devmon_missing_path");
    EXPECT_THROW(factory.get_device_driver("removable_driver"), mc::invalid_arg_exception);
    factory.set_driver_path("");
}

TEST(DeviceDriverFactoryTest, SetDeviceDriverOverridesPreviousDefinition) {
    auto& factory = devmon::device_driver_factory::get_instance();

    auto drivers_a = make_stub_driver_list("DeviceA");
    factory.set_device_driver("override_driver", &drivers_a);

    auto drivers_b = make_stub_driver_list("DeviceB");
    factory.set_device_driver("override_driver", &drivers_b);

    auto& driver = factory.get_device_driver("override_driver");
    EXPECT_EQ(driver.get_devices().size(), 1);

    std::string query = "DeviceB";
    auto&       entry = driver.get_device(query);
    EXPECT_STREQ(entry.device_name, "DeviceB");

    factory.remove_device_driver("override_driver");
}

TEST(DeviceDriverFactoryTest, RemoveNonexistentDriverIsSafe) {
    auto& factory = devmon::device_driver_factory::get_instance();
    EXPECT_NO_THROW(factory.remove_device_driver("nonexistent_driver"));
}

TEST(DeviceDriverFactoryTest, SetDriverPathAffectsLoadFailure) {
    auto& factory = devmon::device_driver_factory::get_instance();

    // 设置自定义驱动路径，触发加载失败分支
    factory.set_driver_path("/tmp/devmon_missing_path");
    EXPECT_THROW(factory.get_device_driver("missing_driver"), mc::invalid_arg_exception);

    // 恢复默认路径，避免影响后续测试
    factory.set_driver_path("./opt/bmc/drivers");
}

TEST(DeviceDriverFactoryTest, SetDeviceDriverUpdatesExistingEntry) {
    auto& factory = devmon::device_driver_factory::get_instance();

    auto original_drivers = make_stub_driver_list("OriginalDevice");
    factory.set_device_driver("update_driver", &original_drivers);

    auto replacement_drivers = make_stub_driver_list("ReplacementDevice");
    factory.set_device_driver("update_driver", &replacement_drivers);

    auto&       driver = factory.get_device_driver("update_driver");
    std::string query  = "ReplacementDevice";
    auto&       entry  = driver.get_device(query);
    EXPECT_STREQ(entry.device_name, "ReplacementDevice");

    factory.remove_device_driver("update_driver");
}